package com.songcan.common.handle;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.songcan.common.annotation.JSON;
import com.songcan.common.exception.AuthException;
import com.songcan.common.exception.CoreException;
import com.songcan.common.exception.PayException;
import com.songcan.common.vo.CoreResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;

import static com.songcan.common.contans.CoreContants.*;

/**
 * 针对控制器进行异常捕获以及返回数据包装
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler   implements ResponseBodyAdvice {


    /**
     * 进行系统处理
     * @param httpServletRequest
     * @param e
     * @return
     */
    @ExceptionHandler(HttpClientErrorException.class)
    public String handle(HttpServletRequest httpServletRequest, Exception e) {
        System.out.println("111111111111111111111111");
        return "404";
    }

    /**
     * 进行控制器异常自定义处理
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {Exception.class,RuntimeException.class})
    @ResponseBody
    private CoreResult defaultErrorHandler(Exception exception) {
        CoreResult coreResult = new CoreResult();

        if(exception.getClass().getName().contains("AuthException")
        || exception.getClass().getName().contains("OAuth2Exception")){
            //针对oauthException做处理
                log.error("message:{}",exception.getMessage());
                coreResult.setCode(ERROR_TOKEN_INVALID);
                coreResult.setMsg(exception.getMessage());
                return  coreResult;
        }
        if (exception instanceof MethodArgumentNotValidException) {
            StringBuilder message = new StringBuilder(ERROR_EXCEPTION);
            MethodArgumentNotValidException methodArgumentNotValidException = MethodArgumentNotValidException.class.cast(exception);
            List<ObjectError> objectErrors = methodArgumentNotValidException.getBindingResult().getAllErrors();
            if (!objectErrors.isEmpty()) {
                message = new StringBuilder();
                for (ObjectError objectError : objectErrors) {
                    message.append(objectError.getDefaultMessage()).append(",");
                }
            }
            log.error(message.toString());
            coreResult.setMsg(message.toString());
            coreResult.setCode(ERROR_PARAM_ILLEGAL);
        }else  if(exception instanceof PayException){
            PayException payException = PayException.class.cast(exception);
              coreResult.setCode(payException.getCode());
              coreResult.setMsg(payException.getMessage());
        } else if (exception instanceof CoreException) {
            log.error(exception.getMessage(), exception);
            coreResult.setException(exception);
        } else if (exception instanceof BindException ) {
            StringBuilder message = new StringBuilder(ERROR_EXCEPTION);
            BindException bindException=BindException.class.cast(exception);
            BindingResult bindingResult = bindException.getBindingResult();
            if(bindingResult.hasErrors()){
                StringBuilder errMess=new StringBuilder();
                bindingResult.getAllErrors().stream().forEach(error->{
                    if (errMess.length()>0){
                        errMess.append(",");
                    }
                    Object[] arguments = error.getArguments();
                    if (arguments.length>0){
                        DefaultMessageSourceResolvable argument = (DefaultMessageSourceResolvable) arguments[0];
                        errMess.append(argument.getDefaultMessage()+":"+error.getDefaultMessage());
                    }
                });
                message=errMess;
            }
            log.error(message.toString(),exception);
            coreResult.setMsg(message.toString());
            coreResult.setCode(ERROR_PARAM_ILLEGAL);
        } else if (exception instanceof ConstraintViolationException
        ){
            ConstraintViolationException constraintViolationException = ConstraintViolationException.class.cast(exception);
            String message=constraintViolationException.getMessage();
            log.error(message,exception);
            coreResult.setMsg(message.split(":")[1]);
            coreResult.setCode(ERROR_PARAM_ILLEGAL);
        } else {
            log.error("异常:{}",exception.getMessage(),exception);
            coreResult.setCode(UNKNOWN);
            coreResult.setMsg(ERROR_EXCEPTION);
        }
        return coreResult;
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
            return true;
    }

    /**
     * 控制器方法返回进行统一封装处理
     * @param body
     * @param methodParameter
     * @param mediaType
     * @param aClass
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @return
     */
    @Override
    @Nullable
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        String clazz = methodParameter.getParameterType().getName();
        CoreResult coreResult = null;


        if(methodParameter.getMethod().getName().equals("error")){
           LinkedHashMap map = LinkedHashMap.class.cast(body);
            coreResult = new CoreResult(map);
            return coreResult;
        }

        if(body instanceof Exception &&body instanceof AuthException){
            return  body;
        }


        if(methodParameter.hasMethodAnnotation(JSON.class)){
            JSON json1 = JSON.class.cast(methodParameter.getMethodAnnotation(JSON.class));
            String json = com.alibaba.fastjson.JSON.toJSON(body).toString();
            String classType = methodParameter.getGenericParameterType().getTypeName();
            String[] exclude = json1.filter().split(",");
            String[] include = json1.include().split(",");
            //针对集合
            SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
            if(classType.contains(Page.class.getName())){
                for (String s : exclude) {
                     filter.getExcludes().add(s);
                }
                coreResult = new CoreResult();
                coreResult.setCode(SUCCESS);
                //如果有分页
                JSONObject page = JSONObject.parseObject(com.alibaba.fastjson.JSON.toJSONString(body));
                if (page.containsKey("records")){
                    JSONArray result = page.getJSONArray("records");
                    page.put("records",JSONObject.parse(JSONObject.toJSONString(result,filter)));
                }
                coreResult.setData(page);
                return coreResult;
            }
            if(!classType.contains(json1.type().getName())){
                coreResult = new CoreResult();
                coreResult.setCode(ERROR_PARAM_ILLEGAL);
                coreResult.setCode("JSON.class(返回对象类型不一致)");
                return coreResult;
            }
            if(body instanceof ArrayList||body instanceof Arrays){
                JSONArray jsonArray = JSONArray.parseArray(json);
                for (String s : exclude) {
                    filter.getExcludes().add(s);
                }
                coreResult =   new CoreResult();
                coreResult.setData(JSONArray.parse(JSONArray.toJSONString(jsonArray,filter)));
                coreResult.setCode(SUCCESS);
                coreResult.setMsg("success");
                return  coreResult;
            }

            JSONObject jsonObject = JSONObject.parseObject(json);
            if(!jsonObject.containsKey("records")){
                for (String s : exclude) {
                    filter.getExcludes().add(s);
                }
                coreResult =   new CoreResult();
                coreResult.setData(JSONArray.parse(JSONArray.toJSONString(jsonObject,filter)));
                coreResult.setCode(SUCCESS);
                coreResult.setMsg("success");
                return new CoreResult(jsonObject);
            }
        }


        if (clazz.contains("String")) {
            coreResult = new CoreResult();
            coreResult.setData(body);
        } else if (clazz.contains("CoreResult")) {
            coreResult = CoreResult.class.cast(body);
        } else {
            coreResult = new CoreResult(body);
        }
        return coreResult;
    }


}
